#ifndef __WIZ_DATATYPE_BUFFER_HPP__SHANHAOBO_19800429__
#define __WIZ_DATATYPE_BUFFER_HPP__SHANHAOBO_19800429__

#include "../../Base/WizBase.hpp"
#include "../../Core/Memory/WizMemory.hpp"
#include "./WizDataTypeArray.hpp"

namespace Wiz
{
    namespace Buffer
    {
        template<class ElementT, int Align = 16, class SizeT = ::Wiz::USize32::Type, class BaseT = ::Wiz::Null::Type>
        struct Type : public BaseT
        {
            WIZ_DECLARE_CLASS_THIS(Type);
            //////////////////////////////////////////////////////////////////////////
            typedef ElementT                tElement;
            typedef const tElement&         tElementIn;
            typedef tElement*               tElementPtr;
            typedef const tElement*         tElementConstPtr;
            typedef SizeT                   tSize;
            typedef tSize                   tCount;
            typedef union
            {
                ::Wiz::Byte::Ptr            BytePtr;
                tElementPtr                 ElementPtr;
            } tUnionPtr;
            typedef union
            {
                ::Wiz::Byte::ConstPtr       BytePtr;
                tElementConstPtr            ElementPtr;
            } tUnionConstPtr;
            //////////////////////////////////////////////////////////////////////////
            ::Wiz::Byte::Ptr                m_BufferPtr;
            tSize                           m_BufferCapacity;
            //////////////////////////////////////////////////////////////////////////
            tUnionPtr                       m_DataPtr;
            tCount                          m_ElementCount;
            //////////////////////////////////////////////////////////////////////////

        public:
            Type() : m_BufferPtr(WIZ_NULLPTR), m_BufferCapacity(0), m_ElementCount(0)
            {
            }
            Type(tCount InCnt)
            {
                this->Create(InCnt);
            }
            virtual ~Type()
            {
                this->Destroy();
            }
        public:
            template<class DataT>
            ::Wiz::Void::Type Copy(DataT* InDataPtr, tCount InCnt)
            {
                ::Wiz::Memory::Copy(this->m_DataPtr.BytePtr, InDataPtr, InCnt * sizeof(DataT));
            }
            ::Wiz::Void::Type Zero()
            {
                ::Wiz::Memory::Zero(this->m_DataPtr.ElementPtr, this->m_ElementCount);
            }
            ::Wiz::Void::Type Fill(tElementIn InEle)
            {
                tUnionPtr UPtr;
                UPtr.BytePtr = this->m_DataPtr.BytePtr; 
                tCount i;
                for (i = 0; i < this->m_ElementCount; i++)
                {
                    UPtr.ElementPtr[i] = InEle;
                }
            }
            Wiz::Void::Type ReplaceElement(tElementIn OldEle, tElementIn NewEle)
            {
                tUnionPtr UPtr;
                UPtr.BytePtr = this->m_DataPtr.BytePtr; 
                tCount i;
                for (i = 0; i < this->m_ElementCount; i++)
                {
                    if (UPtr.ElementPtr[i] == OldEle)
                    {
                        UPtr.ElementPtr[i] = NewEle;
                    }
                }
            }
            Wiz::Void::Type FindAllElements(tElementIn InEle, ::Wiz::Array::Type<tCount>& OutArray)
            {
                tUnionPtr UPtr;
                UPtr.BytePtr = this->m_DataPtr.BytePtr; 
                tCount i;
                for (i = 0; i < this->m_ElementCount; i++)
                {
                    if (UPtr.ElementPtr[i] == InEle)
                    {
                        OutArray.PushBack(i);
                    }
                }
            }
            ::Wiz::Void::Type Clear()
            {
                this->m_ElementCount = 0;
            }
        public:
            /************************************************************************/
            /*                                                                      */
            /************************************************************************/
            tSize CalcCapacity(tCount InCnt) const
            {
                return ::Wiz::Utils::AlignUp<tSize, Align>(InCnt * sizeof(tElement));
            }

            tSize CalcMaxElementCount() const
            {
                return m_BufferCapacity / sizeof(tElement);
            }

            /************************************************************************/
            /*                                                                      */
            /************************************************************************/
            ::Wiz::Bool::Type Create(tSize InCnt)
            {
                WIZ_ASSERT(InCnt > 0);

                this->m_ElementCount = InCnt;
                this->m_BufferCapacity = CalcCapacity(InCnt);


                this->m_BufferPtr = ::Wiz::Cast::Static<::Wiz::Byte::Ptr>(malloc(size_t(this->m_BufferCapacity + Align)));
                this->m_DataPtr.BytePtr = ::Wiz::Utils::AlignUp<::Wiz::Byte::Ptr, Align>(this->m_BufferPtr);
                if (NotValid())
                {
                    ::Wiz::Exception::Throw(::Wiz::Exception::BadAlloc());
                    return ::Wiz::Bool::False;
                }
                return ::Wiz::Bool::True;
            }

            ::Wiz::Bool::Type Expand(tSize InCnt)
            {
                WIZ_ASSERT(InCnt > 0);

                tSize TempCapacity = CalcCapacity(InCnt + CalcMaxElementCount());

                ::Wiz::Byte::Ptr        TempBufferPtr   = WIZ_NULLPTR;
                ::Wiz::Byte::FixedPtr   TempDataBytePtr = ::Wiz::Memory::AllocTypeLessWithAlign<Align>(TempCapacity, TempBufferPtr);
                if (::Wiz::NotValidPtr(TempBufferPtr))
                {
                    return ::Wiz::Bool::False;
                }

                if (::Wiz::IsValidPtr(this->m_BufferPtr))
                {
                    if (m_ElementCount > 0)
                    {
                        ::Wiz::Memory::Copy(TempDataBytePtr, this->m_DataPtr.BytePtr, m_ElementCount * sizeof(tElement));
                    }
                    free(this->m_BufferPtr);
                }

                this->m_BufferPtr = TempBufferPtr;
                this->m_DataPtr.BytePtr = TempDataBytePtr;
                this->m_BufferCapacity = TempCapacity;

                return ::Wiz::Bool::True;
            }

            ::Wiz::Bool::Type Expand()
            {
                return Expand(m_ElementCount);
            }

            /************************************************************************/
            /*                                                                      */
            /************************************************************************/
            ::Wiz::Void::Type Destroy()
            {
                if (::Wiz::IsValidPtr(this->m_BufferPtr))
                {
                    free(this->m_BufferPtr);
                    this->m_BufferPtr = WIZ_NULLPTR;
                    this->m_BufferCapacity = 0;
                }
            }

            /************************************************************************/
            /*                                                                      */
            /************************************************************************/
            ::Wiz::Bool::Type Resize(tCount InCnt)
            {
                WIZ_ASSERT(InCnt > 0);

                if (::Wiz::NotValidPtr(this->m_BufferPtr))
                {
                    return this->Create(InCnt);
                }

                tSize NewBufferSize = this->CalcCapacity(InCnt);

                if (NewBufferSize <= this->m_BufferCapacity)
                {
                    this->m_ElementCount = InCnt;
                    return ::Wiz::Bool::True;
                }

                if (this->m_ElementCount == 0)
                {
                    this->Destroy();
                    return this->Create(InCnt);
                }

                if (this->Expand())
                {
                    this->m_ElementCount = InCnt;
                    return ::Wiz::Bool::True;
                }
                return ::Wiz::Bool::False;
            }

            /************************************************************************/
            /*                                                                      */
            /************************************************************************/
            tElementPtr GetPtr()
            {
                return this->m_DataPtr.ElementPtr;
            }
            tElementConstPtr GetPtr() const
            {
                return this->m_DataPtr.ElementPtr;
            }

            tCount GetElementCount() const
            {
                return this->m_ElementCount;
            }

            tSize Size() const
            {
                return this->m_ElementCount;
            }

            /************************************************************************/
            /*                                                                      */
            /************************************************************************/
            ::Wiz::Bool::Type IsValid()
            {
                return ::Wiz::IsValidPtr(this->m_BufferPtr);
            }
            ::Wiz::Bool::Type NotValid()
            {
                return ::Wiz::NotValidPtr(this->m_BufferPtr);
            }
        }; /// end of struct Type
    } /// end of namespace Buffer

    namespace ByteBuffer
    {
        typedef ::Wiz::Buffer::Type<::Wiz::Byte::Type>  Type;
        WIZ_DECLARE(::Wiz::ByteBuffer::Type);
    } /// end of namespace ByteBuffer
} /// end of namespace Wiz

#endif /*__WIZ_DATATYPE_BUFFER_HPP__SHANHAOBO_19800429__*/

